local M = {}
local callbacks = {
	"draw",
	"errhand",
	"errorhandler",
	"load",
	"lowmemory",
	"quit",
	"run",
	"threaderror",
	"update",

	"directorydropped",
	"filedropped",
	"focus",
	"mousefocus",
	"resize",
	"visible",

	"keypressed",
	"keyreleased",
	"textedited",
	"textinput",

	"mousemoved",
	"mousepressed",
	"mousereleased",
	"wheelmoved",

	"gamepadaxis",
	"gamepadpressed",
	"gamepadreleased",
	"joystickadded",
	"joystickaxis",
	"joystickhat",
	"joystickpressed",
	"joystickreleased",
	"joystickremoved",

	"touchmoved",
	"touchpressed",
	"touchreleased",
}

-- revert these back to backup asap
-- local protected = {"errhand", "errorhandler", "run"} 


M.backupCallbacks = function()
	local backup = {}
	for k, v in ipairs(callbacks) do backup[v] = love[v] end
	return backup
end

local restoreCallbacks= function(backup)
	for k,v in ipairs(callbacks) do love[v] = backup[v] end
end

local clearCallbacks = function()
	for k,v in ipairs(callbacks) do love[v] = nil end
end

local avReset = function()
	love.audio.stop()
	love.mouse.setCursor()
	love.mouse.setVisible(true)
	love.mouse.setGrabbed(false)
	love.mouse.setRelativeMode(false)
	love.graphics.reset()
end

local restoreState = function(backup)
	if not backup then -- nothing to go back
		love.quit = nil
		love.event.quit("restart")
		return
	end
	restoreCallbacks(backup)
	avReset()
	if love.load then love.load() end
end

local dummy = function() end

local quitHook = function(fn, backup)
	fn = fn or dummy
	return function()
		fn()
		restoreState(backup)
		return true
	end
end

local errorHandler = function(msg)
	print(msg)
	return function()
		return "restart"
	end
end

local keypressedHook = function(fn, backup)
	fn = fn or dummy
	return function(k)
		if k == "escape" then restoreState(backup) end
		fn(k)
		return
	end
end

local errhand = love.errhand

M.run = function(file, backup, ...)
	if not love.filesystem.getInfo(file) then
		return false, ("File '%s' does not exit"):format(file)
	end
	
	local ok, result = pcall(love.filesystem.load, file)
	if not ok then
		return false, ("Load error: '%s'"):format(result)
	end
	
	clearCallbacks()
	
	ok, result = pcall(result, ...)
	if not ok then
		restoreState(backup)
		return false, ("Run error: '%s'"):format(result)
	end

	-- hooks
	love.keypressed = keypressedHook(love.keypressed, backup)
	love.quit = quitHook(love.quit, backup)
	-- overrides
	love.errhand = backup and backup.errhand or errhand
	love.errorhandler = errorHandler
	--love.run = nil -- no effect

	avReset()
	
	if love.load then love.load() end
	return true
end

return M

